package defectsim.resources;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.vecmath.Vector2d;

import repast.simphony.parameter.Parameters;
import repast.simphony.space.continuous.NdPoint;
import defectsim.Globals;
import defectsim.SimulationEnvironment;
import defectsim.agents.DefectAgent;
import defectsim.agents.Dislocation;
import defectsim.agents.EdgeDislocation;

public class InteractionForce extends DefectResource implements Force {

	private SimulationEnvironment env = SimulationEnvironment.getInstance();

	private List<Dislocation> dislocationList = new ArrayList<Dislocation>();
	private HashMap<String, Vector2d> dislocationForceMap = new HashMap<String, Vector2d>();
	private ForceType fType;
	private double maxPRadius;
	private double minPRadius;
	private double boxLength;

	/*
	 *  Interaction force class is singleton
	 */
	private static InteractionForce _instance = null;

	private InteractionForce(){
		fType = ForceType.Interaction;
	}

	static public InteractionForce getInstance() {
		if(_instance == null) {
			_instance = new InteractionForce();
		}
		return _instance;
	}
	
	public void initialize(){
		Parameters p = env.getParameters();
		maxPRadius = (Double)p.getValue("maxcutoffradius");
		minPRadius = (Double)p.getValue("mincutoffradius") * env.getMaterial().getBurgersVector() * Globals.NANOMETER_TO_MICROMETER;
		boxLength = (Double)p.getValue("spacelength");		
	}

	public Vector2d compute(DefectAgent defectAgent) {
		Vector2d lr = new Vector2d();
		double b = 0;

		if (!dislocationForceMap.containsKey(defectAgent.toString())){
			if (defectAgent instanceof EdgeDislocation){
				EdgeDislocation d = (EdgeDislocation)defectAgent;
				b = d.getUnitBurgersVector() * env.getMaterial().getBurgersVector();
				lr = calculateTotalLongRangeStress(d, d.getLongRangeNeighbours());
			}
		}
		else 		
			if (defectAgent instanceof EdgeDislocation){
				EdgeDislocation d = (EdgeDislocation)defectAgent;
				b = d.getUnitBurgersVector() * env.getMaterial().getBurgersVector();
				lr = dislocationForceMap.get(defectAgent.toString());
			}
		return new Vector2d(b * lr.x, b * lr.y);
	}

	private Vector2d calculateTotalLongRangeStress(Dislocation dislocationI, List<Dislocation> longRangeNeighbours){
		Vector2d totalInteractionStress  = new Vector2d();
		NdPoint pointI = env.getLocation(dislocationI);
		double A = (env.getMaterial().getShearModulus() * env.getMaterial().getBurgersVector()) / 
		(2 * Math.PI * (1 - env.getMaterial().getPoissonRatio()));
		for (Dislocation dislocationJ : longRangeNeighbours) {
			NdPoint pointJ = env.getLocation(dislocationJ);
			if (pointJ != null && dislocationI != dislocationJ){
				Vector2d interactionStress = InteractionStress.compute(pointI, pointJ, dislocationI.getDirection(), 
						dislocationJ.getDirection(), dislocationJ.getUnitBurgersVector(), 
						A, minPRadius, maxPRadius, boxLength);
				totalInteractionStress.add(interactionStress);
			}
		}
		return totalInteractionStress;
	}

	public Vector2d getForce(Dislocation dis){
		Vector2d lr = new Vector2d();
		if (!dislocationForceMap.containsKey(dis.toString()))
			lr = calculateTotalLongRangeStress(dis, ((EdgeDislocation)dis).getLongRangeNeighbours());
		else
			lr = dislocationForceMap.get(dis.toString());
		return lr;
	}

	@Override
	public ForceType getForceType() {
		return fType;
	}

	@Override
	public void update() {
		dislocationList = env.getDislocations();
		if (!dislocationForceMap.isEmpty())
			dislocationForceMap.clear();
		for (Dislocation d : dislocationList){
			List<Dislocation> neighbours = ((EdgeDislocation)d).getLongRangeNeighbours();
			Vector2d lr = calculateTotalLongRangeStress(d, neighbours);
			dislocationForceMap.put(d.toString(), lr);
		}

	}

	@Override
	public Vector2d getStress(DefectAgent da) {
		Vector2d lr = new Vector2d();

		if (!dislocationForceMap.containsKey(da.toString())){
			if (da instanceof EdgeDislocation){
				EdgeDislocation d = (EdgeDislocation)da;
				if (d != null)
					lr = calculateTotalLongRangeStress(d, d.getLongRangeNeighbours());
			}
		}
		else 		
			if (da instanceof EdgeDislocation){
				EdgeDislocation d = (EdgeDislocation)da;
				if (d != null)
					lr = dislocationForceMap.get(da.toString());
			}
		return new Vector2d(lr.x, lr.y);
	}
}
